#! /usr/bin/env bash
CURDIR=$(cd $(dirname $0); pwd)


### 本地测试 kitexcall ###
# 0. clone 并 编译最新的 kitexcall
#   git clone git@github.com:kitex-contrib/kitexcall.git
#   go build
# 1. clone proto 仓库到 rpc-server 的平级目录下：
#   ├── proto
#   ├── rpc-server
#
# 2. cd rpc-server/kitexcall_test
# 3. 启动 rpc-server 服务
# 4. 下载最新的https://github.com/kitex-contrib/kitexcall 代码，编译 kitexcall，老版本的有些命令参数不支持
# 5. 执行 ./local_kitexcall.sh
# 6. 指定 -m 参数，只测试指定的方法
#    例如：./local_kitexcall.sh -m "ResourceList"
# 7. 指定 -d 参数，只测试指定目录下的 JSON 文件
#    例如：./local_kitexcall.sh -d "resource"
# Parse command line arguments

function result_assert() {
  # 断言逻辑，解析json_assert, 结构为：
  # {
  #   "assertions": [
  #     {
  #       "key": ".result |= map(select(.name | contains(\"8f0d5fc9-59b3-4481-9c88-122514d2ce18\")))",
  #       "value": true
  #     }
  #   ]
  # }
  # 判断是否包含 assertions 字段，且是否是数组，如果是数组，则遍历数组中的每个元素，判断是否包含 key 和 value 字段，用jq命令使用 key的值作为jq表达式，判断jq表达式的结果是否等于 value 的值

  inner_json_assert=$1
  inner_result=$2
  if [[ ! "$inner_result" == *"$command_fail"*  ]]; then
      case_result+=("✅ response pass: [$rpc_method] ($json_file)")
  else
      case_result+=("❌ response fail: [$rpc_method] ($json_file) ")
      return 1
  fi
   if [ "$inner_json_assert" != "null" ] || [ ! -z "$inner_json_assert" ]; then
      # 确保 assertions 是紧凑的 JSON 行
      assertions=$(echo "$inner_json_assert" | jq -c '.assertions[] // empty')
       IFS=$'\n' # 让循环按换行符分割，而不是按空格
       for assertion in $assertions; do
          k=$(echo "$assertion" | jq -r '.key')
          v=$(echo "$assertion" | jq -r '.value')

          if [ ! -z "$k" ] && [ ! -z "$v" ]; then
            assertion_result=$(echo "$inner_result" | jq "$k") # 使用 key 操作 result
            if [ ! "$assertion_result" == "$v" ]; then
              echo "❌ assert fail: [$rpc_method] ($json_file): $k != $v"
              assert_err_msg="   ❌ assert fail: $k != $v"
              return 1
            fi
          fi
      done
    else
      return 0
    fi
}

# Initialize variables
test_method=""
test_dir=""
while getopts "m:d:" opt; do
  case $opt in
    m)
      # shellcheck disable=SC2034
      test_method=$OPTARG
      ;;
    d)
      test_dir=$OPTARG
      ;;
    *)
      usage
      ;;
  esac
done
echo "test_method: $test_method"
# 测试结果数组
declare -a case_result=()
declare -A captured_vars
# 指定 JSON 文件所在目录
json_dir="$CURDIR/test_cases"
# 如果 test_dir 存在，则把 test_dir 拼接在 json_dir 后面
if [ ! -z "$test_dir" ]; then
  json_dir="$json_dir/$test_dir"
fi
command_fail="Failed"
endpoint="127.0.0.1:8890"
tenant_uuid=""
# 处理 meta 文件
for meta_file in "$CURDIR/meta-persistent.json" "$CURDIR/meta.json" "$dir/meta-persistent.json" "$dir/meta.json"; do
    if [ -f "$meta_file" ]; then
      # 读取文件的JSON格式内容，获取 X_META_INFO_TENANT_UUID 的值，赋值给 tenant_uuid
      tenant_uuid=$(cat "$meta_file" | jq -r '.X_META_INFO_TENANT_UUID')
    fi
done
echo "tenant_uuid: $tenant_uuid"
# 遍历目录中的每个 JSON 文件
  while IFS= read -r json_file;  do
    dir=$(dirname "$json_file")

    commander=""
    timestamp=$(date +%s)
    random=$(($RANDOM % 1000000))
    unique_suffix="${timestamp}-${random}"
    # 遍历每个 JSON 对象
    while IFS= read -r item; do
      # 提取 rpc_method
      rpc_method=$(echo "$item" | jq -r '.rpc_method')
      if [ ! -z "$test_method" ] && [ "$rpc_method" != "$test_method" ]; then
          continue
      fi
      # 构造 JSON 格式的请求参数
      json_params=$(echo "$item" | jq -r '.rpc_body')

      # 替换 json_params 字符串 中的 {{unique_suffix}} 为当前的 unique_suffix
      json_params=$(echo "$json_params" | sed "s#{{tenant_uuid}}#$tenant_uuid#g" | tr -d '\n')
      json_params=$(echo "$json_params" | sed "s#{{unique_suffix}}#$unique_suffix#g" | tr -d '\n')

      # 构造 JSON 格式的断言参数
      json_assert=$(echo "$item" | jq -r '.rpc_assert // empty')
      json_assert=$(echo "$json_assert" | sed "s#{{tenant_uuid}}#$tenant_uuid#g" | tr -d '\n')
      json_assert=$(echo "$json_assert" | sed "s#{{unique_suffix}}#$unique_suffix#g" | tr -d '\n')

      # 替换 json_params 字符串 中的 {{var_name}} 为当前的 var_name
      for var_name in "${!captured_vars[@]}"; do
        # 处理请求参数
        json_params=$(echo "$json_params" | sed "s#{{${var_name}}}#${captured_vars[$var_name]}#g" | tr -d '\n')
        json_params=$(echo "$json_params" | sed "s#\"<<${var_name}>>\"#${captured_vars[$var_name]}#g" | tr -d '\n')
        json_params=$(echo "$json_params" | sed "s#<<${var_name}>>#${captured_vars[$var_name]}#g" | tr -d '\n')

        # 处理断言参数
        json_assert=$(echo "$json_assert" | sed "s#{{${var_name}}}#${captured_vars[$var_name]}#g" | tr -d '\n')
        json_assert=$(echo "$json_assert" | sed "s#\"<<${var_name}>>\"#${captured_vars[$var_name]}#g" | tr -d '\n')
        json_assert=$(echo "$json_assert" | sed "s#<<${var_name}>>#${captured_vars[$var_name]}#g" | tr -d '\n')
      done
      # 打印结果
      echo "Method: $rpc_method"
      echo "JSON Params: "
      echo "$json_params" | jq
      echo "JSON assert: "
      echo "$json_assert" | jq
      echo "---------------------------------------------------------------------------
      "

      commander="kitexcall -t protobuf -transport TTHeader -include-path \"$CURDIR/../../proto/middle/\" -p \"$CURDIR/../../proto/middle/network/network.proto\" -m NetworkService/\"$rpc_method\" -e $endpoint  -d '$json_params' "
      # 判断当前目录下是否存在meta-persistent.json文件，如果存在则将meta-persistent.json文件的内容作为metainfo传递给kcall
      # 其中kitecall 的设置meta 的参数格式为：-meta-persistent key=value，且 key=value 可以为多个，用空格隔开。
      # 先判断文件是否存在，然后读取文件的JSON格式内容，将内容转换为key=value格式，然后传递给kcall

       # 处理 meta 文件
      for meta_file in "$CURDIR/meta-persistent.json" "$CURDIR/meta.json" "$dir/meta-persistent.json" "$dir/meta.json"; do
          if [ -f "$meta_file" ]; then
              meta=$(cat "$meta_file" | jq -r 'to_entries | map("\(.key)=\(.value|tostring)") | join(" ")')
              if [[ "$meta_file" == *"persistent"* ]]; then
                  commander="$commander -meta-persistent $meta"
              else
                  commander="$commander -meta $meta"
              fi
          fi
      done

      commander="$commander 2>&1"
      # 打印出 kitexcall 命令的输出
      echo "commander: $commander"

      # 执行 kitexcall 命令，使用当前 JSON 文件的内容
      origin_result=$(eval "$commander")
      result_desc=$(echo "$origin_result" | head -n 1)
      echo "result_desc: $result_desc"
      if [[ "$result_desc" == *"$command_fail"*  ]]; then
          echo "command fail"
          case_result+=("❌ command fail: [$rpc_method] ($json_file) ")
          continue
      fi
      result=$(echo "$origin_result" | tail -n +2)
      # 打印出 kitexcall 命令的输出
      printf "\nresult: \n"
      echo "$result" | jq
      # 如果 result 不为空，再执行下面的处理
      if [ -z "$result" ] || [ "$result" == "null" ] || [ "$(echo "$result" | jq 'length == 0')" == "true" ]; then
          echo "result is empty"
      else
        captures=$(echo "$item" | jq -r '.rpc_result_captures | to_entries[]? | "\(.key)=\(.value)"')
        if [ ! -z "$captures" ] ; then
          for capture in $captures; do
            var_name=$(echo "$capture" | cut -d'=' -f1)
            result_key=$(echo "$capture" | cut -d'=' -f2)
            capture_value=$(echo "$result" | jq -r ".$result_key")
            if [ "$capture_value" != "null" ] && [ ! -z "$capture_value" ]; then
              captured_vars["$var_name"]=$capture_value
              echo "$var_name: ${captured_vars[$var_name]}"
            fi
          done
        fi
      fi
      assert_err_msg=""
      result_assert "$json_assert" "$result"
      # 判断函数是否成功
      if [ $? -eq 0 ]; then
        case_result+=("   ✅ assert pass  ")
      else
        case_result+=("$assert_err_msg")
      fi
      echo "---------------------------------------------------------------------------
      "
    done < <(jq -c '.[]' "$json_file")
  done < <(find "$json_dir" -type f -name "*.json"| sort )

  # 逐行输出数组中的每个元素
  for element in "${case_result[@]}"; do
    echo "$element"
  done
